package ldh.im.fxgui.controller;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import ldh.im.ImConfig;
import ldh.im.dto.base.Dto;
import ldh.im.dto.business.av.AvInvitationReq;
import ldh.im.dto.business.av.ChatDataReq;
import ldh.im.dto.business.item.InvitationResultList;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.data.pojo.ImUser;
import ldh.im.fxbase.rpc.service.RpcMessageService;
import ldh.im.fxbase.rpc.service.RpcWebRtcService;
import ldh.im.fxbase.service.AudioSocketService;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.handle.AvHandler;
import ldh.im.fxgui.util.UiUtil;
import ldh.im.fxgui.util.UserHeadImageUtil;
import ldh.im.util.ByteCompressUtil;
import ldh.im.util.LocalSeqUtil;
import ldh.im.util.Sets;
import ldh.rpc.Receive;
import ldh.rpc.client.RpcClientBootstrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

// 语音聊天
public class AudioController implements Receive<Dto>, Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(AudioController.class);

    @FXML private Pane container;
    @FXML private FlowPane flowPane;

    private RpcClientBootstrap rpcClientBootstrap;
    private ImSession imSession;
    private Set<String> userIdSet;

    private volatile String groupCode;
    private volatile AudioSocketService playAudioSocketService = null;  // 播放语音
    private volatile AudioSocketService audioSocketService = null;      // 录制语音
    private ExecutorService avReceiveExecutorService = Executors.newCachedThreadPool();

    private AvHandler avHandler = null;

    public void setParams(RpcClientBootstrap rpcClientBootstrap, ImSession imSession, String groupId, Set<String> userIdSet) {
        this.rpcClientBootstrap = rpcClientBootstrap;
        this.imSession = imSession;
        this.userIdSet = userIdSet;

        avHandler = new AvHandler(rpcClientBootstrap);

        if (imSession.isP2p()) {
            initP2pPane();
        } else {
            initGroupPane();
        }

        ThreadPoolUtil.submit(()->{
            ExceptionUtil.catchException(()->{
                String userId = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService().getCache(ImConfig.LOGIN_USER_ID).toString();
                avHandler.login();

                if (groupId == null) { // 发起
                    groupCode = avHandler.createGroup(LocalSeqUtil.createLocalSeq(), userIdSet);

                    AvInvitationReq avInvitationReq = new AvInvitationReq();
                    avInvitationReq.setFromUserId(userId);
                    userIdSet.remove(userId);
                    avInvitationReq.setUserIds(userIdSet);
                    RpcWebRtcService rpcWebRtcService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcWebRtcService.class);
                    InvitationResultList avInvitationResultList = rpcWebRtcService.sendAvInvitation(avInvitationReq);

                    long offlineCount = avInvitationResultList.getInvitationResultList().stream().filter(ir->ir.isOffline()).count();
                    if (offlineCount == avInvitationReq.getUserIds().size()) {
                        FxThreadUtil.runLater(()->{
                            DialogUtil.alert("警告", "用户不在线，不能语音聊天");
                            this.close();
                        });
                        return null;
                    }
                    startAudio();
                } else {   // 接受
                    avHandler.addGroup(groupId, Sets.asSet(userId));
                }
                return null;
            }, "语音聊天");
        });
    }

    @FXML
    public void close() {
        ThreadPoolUtil.submit(()->{
            try {
                avHandler.exitGroup(groupCode);
                if (rpcClientBootstrap != null) {
                    rpcClientBootstrap.close();
                }
            } catch (Exception e) {
            }

        });

        if (audioSocketService != null) {
            audioSocketService.close();
        }

        if (playAudioSocketService != null) {
            playAudioSocketService.close();
        }

        Window window = container.getScene().getWindow();
        if (window instanceof Stage) {
            ((Stage) window).close();
        }

        if (avReceiveExecutorService != null) {
            avReceiveExecutorService.shutdownNow();
        }
    }

    @Override
    public void receive(Dto data) {
        if (!(data instanceof ChatDataReq)) return;
        if (playAudioSocketService == null) {
            playAudioSocketService = new AudioSocketService();
            playAudioSocketService.startStream();
        }
        avReceiveExecutorService.execute(()->{
            ChatDataReq chatDataReq = (ChatDataReq) data;
            try {
                byte[] unzipBytes = ByteCompressUtil.unZip(chatDataReq.getData());
                if (unzipBytes == null) return;
                playAudioSocketService.playAudio(unzipBytes, unzipBytes.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void startAudio() {
        BiConsumer<byte[], Integer> consumer = (bytes, length)->{
            try {
                byte[] zipBytes = ByteCompressUtil.zip(bytes, 0, length);
                LOGGER.info("send audio message:{}, {}", length, zipBytes.length);

                avHandler.sendAudioData(groupCode, zipBytes, zipBytes.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        audioSocketService = new AudioSocketService(consumer);
        audioSocketService.startRecord();
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {

    }

    private void initGroupPane() {
        List<Label> labelList = userIdSet.stream().map(userId-> buildAvUserImage(userId)).collect(Collectors.toList());
        flowPane.getChildren().addAll(labelList);
        flowPane.setVgap(10);
        flowPane.setHgap(10);
    }

    private void initP2pPane() {
        VBox vBox = new VBox();
        vBox.setAlignment(Pos.CENTER);

        vBox.getChildren().addAll(buildAvUserImage(imSession.getFromUserId()));
        vBox.setSpacing(10);
        flowPane.getChildren().addAll(vBox);
    }

    private Label buildAvUserImage(String userId) {
        ImUser imUser = ImCacheFactory.getInstance().getUser(userId);
        ImageView imageView = new ImageView();
        String headImg = imUser.getHeadImg();
        Image image = UserHeadImageUtil.getHeadImage(headImg);
        imageView.setImage(image);
        imageView.setFitWidth(100);
        imageView.setFitHeight(100);

        Label label = new Label(imUser.getUserName());
        label.setGraphic(imageView);
        label.setContentDisplay(ContentDisplay.TOP);
        return label;
    }
}
